home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Speccy ClassiX 1998
/
Speccy ClassiX 98.iso
/
amiga_system
/
the_aminet
/
dev
/
gcc
/
ixemulsrc.lha
/
ixemul-41.4
/
library
/
__plock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-09-27
|
17KB
|
595 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* __plock.c,v 1.1.1.1 1994/04/04 04:30:12 amiga Exp
*
* __plock.c,v
* Revision 1.1.1.1 1994/04/04 04:30:12 amiga
* Initial CVS check in.
*
* Revision 1.2 1992/08/09 20:38:34 amiga
* change to use 2.x header files by default
*
* Revision 1.1 1992/05/14 19:55:40 mwild
* Initial revision
*
*/
/* 20-jan-92 -mw- revamped to emulate GetDeviceProc() under 1.3
* new way to find out whether IsFileSystem(name)
* 14-mar-92 -mw- limited /sys -> sys: mapping. `/' is now treated
* as undefined path, to be compatible to a later
* version, where it will be a virtual directory
* of devices and logicals
*/
/*
* Lock() and LLock() emulation. Takes care of expanding paths that contain
* symlinks.
* Call __plock() if you need a lock to the parent directory as used in
* other packets, that way you always get the "right" thing
*/
#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <stdlib.h>
#include <string.h>
/* don't need async packets in here, no shared file packets are in use */
#define __srwport (u.u_sync_mp)
static struct DevProc *get_device_proc (char *, struct DevProc *, int *);
static void free_device_proc (struct DevProc *);
static int unslashify (char *);
BPTR
__plock (char *file_name, int (*last_func)(), void *last_arg)
{
BPTR parent_lock;
struct MsgPort *handler;
struct StandardPacket *sp;
unsigned char *bstr;
char *sep, *next, *cp;
int len;
/* true when we're processing the last element of name */
int is_last;
/* true after first pass, we should unlock all locks except the one
* we get as a sideeffect of DeviceProc */
int unlock_parent;
int link_levels;
int no_error;
BPTR result;
int res_res2;
int omask;
int split_name;
struct DevProc *dp;
char *orig_name, *name;
KPRINTF (("__plock: file_name = %s, last_func = $%lx\n",
file_name ? file_name : "(none)", last_func));
/* ``fix'' until I find the real problem in pdksh.. */
if (! file_name)
return 0;
/* need to operate on a backup of the passed name, so I can do
* /sys -> sys: conversion in place */
name = alloca (strlen (file_name) + 1);
strcpy (name, file_name);
orig_name = name;
/* now get a LONG aligned packet */
sp = alloca (sizeof(*sp)+2);
sp = LONG_ALIGN (sp);
__init_std_packet(sp);
/* allocate one BSTR-buffer. A length of 255 is enough, since a bstr
* can't address any more ;-) */
bstr = alloca (256 + 2);
bstr = LONG_ALIGN (bstr);
/* NOTE: although we don't use any DOS calls here, we have to block
* any signals, since the locks we obtain in this function have to
* be freed before anything else can be done. This function is
* *not* reentrant in the sense that it can be interrupted without
* being finished */
omask = syscall (SYS_sigsetmask, ~0);
dp = 0;
retry_multi_assign:
name = orig_name;
if (ix.ix_translate_slash && unslashify (name) < 0)
{
result = 0;
res_res2 = ERROR_OBJECT_NOT_FOUND;
dp = 0;
goto do_return;
}
if (! strcmp (name, "*") || ! strcasecmp (name, "console:"))
{
handler = (struct MsgPort *)(((struct Process *)FindTask (0))->pr_ConsoleTask);
parent_lock = 0;
KPRINTF (("__plock: console override, handler $%lx.\n", handler));
name = "CONSOLE:";
if (dp) free_device_proc (dp);
split_name = 0;
dp = 0;
}
else if (! strcasecmp (name, "nil:") || ! strcasecmp (name, "/nil") ||
! strcmp (name, "/dev/null") || ! strcmp (name, "dev:null"))
{
result = 0;
res_res2 = 4242; /* special special ;-)))) */
dp = 0;
goto do_return;
}
else
{
dp = get_device_proc (name, dp, &split_name);
KPRINTF (("__plock: gdp(%s) -> $%lx",name,dp));
handler = dp ? dp->dvp_Port : 0;
parent_lock = dp ? dp->dvp_Lock : 0;
if (handler)
KPRINTF ((" handler = $%lx, parent_lock = $%lx, is_fs = %ld\n", handler, parent_lock, split_name));
else
KPRINTF (("\n"));
}
is_last = 0;
unlock_parent = 0;
link_levels = 0;
result = 0;
if (! handler)
{
res_res2 = ERROR_OBJECT_NOT_FOUND;
goto do_return;
}
link_levels = 0;
/* this seems logical, doesn't it? don't know ;-)) */
sep = index (name, ':');
if (sep) name = sep+1;
do
{
KPRINTF (("__plock: solving for %s.\n", name));
if (split_name)
{
/* fetch the first part of "name", thus stopping at either a : or a /
* next points at the start of the next directory component to be
* processed in the next run
*/
sep = index (name, ':');
if (sep)
{
sep++; /* the : is part of the filename */
next = sep;
}
else
{
sep = index (name, '/');
/* map foo/bar/ into foo/bar, but keep foo/bar// */
if (sep && sep[1]==0)
{
is_last = 1;
next = sep;
}
else if (! sep)
{
sep = name + strlen (name);
next = sep;
is_last = 1;
}
else
{
if (ix.ix_translate_slash)
for (next = sep + 1; *next == '/'; next ++) ;
else
next = sep + 1;
/* if the slash is the first character, it means "parent",
* so we have to pass it literally to Lock() */
if (sep == name) sep = next;
}
}
}
else
{
sep = name + strlen (name);
is_last = 1;
}
len = sep - name;
if (len) bcopy (name, bstr + 1, len);
*bstr = len;
if (ix.ix_translate_dots)
{
/* turn a ".." into a "/", and a "." into a "" */
if (bcmp (bstr, "\2..", 3) == 0)
{
bstr[0] = 1; bstr[1] = '/';
}
else if (bcmp (bstr, "\1.", 2) == 0)
bstr[0] = 0;
}
do
{
sp->sp_Pkt.dp_Port = __srwport;
if (! is_last)
{
sp->sp_Pkt.dp_Type = ACTION_LOCATE_OBJECT;
sp->sp_Pkt.dp_Arg1 = parent_lock;
sp->sp_Pkt.dp_Arg2 = CTOBPTR(bstr);
sp->sp_Pkt.dp_Arg3 = ACCESS_READ;
PutPacket(handler, sp);
__wait_sync_packet(sp);
no_error = sp->sp_Pkt.dp_Res1 > 0;
}
else
if (! (*last_func)(sp, handler, parent_lock, CTOBPTR (bstr),
last_arg, &no_error)) break;
/* if no error, fine */
if (no_error) break;
/* else check whether ordinary error or really symlink */
if (sp->sp_Pkt.dp_Res2 != ERROR_IS_SOFT_LINK) break;
/* read the link. temporarily use our bstr as a cstr, thus setting
* a terminating zero byte and skipping the length byte */
bstr[*bstr + 1] = 0;
sp->sp_Pkt.dp_Port = __srwport;
sp->sp_Pkt.dp_Type = ACTION_READ_LINK;
sp->sp_Pkt.dp_Arg1 = parent_lock;
sp->sp_Pkt.dp_Arg2 = (long) (bstr + 1); /* read as cstr */
sp->sp_Pkt.dp_Arg3 = (long) (bstr + 1); /* write as cstr, same place */
sp->sp_Pkt.dp_Arg4 = 255; /* what a BSTR can address */
PutPacket(handler, sp);
__wait_sync_packet(sp);
/* error (no matter which...) couldn't read the link */
if (sp->sp_Pkt.dp_Res1 <= 0)
{
/* this is our error-"lock", so make sure it is really zero */
sp->sp_Pkt.dp_Res1 = 0;
break;
}
/* oky, new name. Set up as bstr and retry to lock it */
*bstr = sp->sp_Pkt.dp_Res1;
/* if the read name is absolute, we may have to change the
* handler and the parent lock. Check for this */
bstr[*bstr + 1] = 0;
/* this isn't enabled by default, because it makes normal dos calls
* fail miserably */
if (ix.ix_translate_symlinks && unslashify (bstr + 1) < 0)
{
sp->sp_Pkt.dp_Res1 = 0;
sp->sp_Pkt.dp_Res2 = ERROR_OBJECT_NOT_FOUND;
break;
}
if (cp = index ((char *)bstr + 1, ':'))
{
if (unlock_parent)
{
sp->sp_Pkt.dp_Port = __srwport;
sp->sp_Pkt.dp_Type = ACTION_FREE_LOCK;
sp->sp_Pkt.dp_Arg1 = parent_lock;
PutPacket(handler, sp);
__wait_sync_packet(sp);
}
/* if this is ":foobar", then the handler stays the same, and the
* parent_lock gets zero. Don't need get_device_proc() to find
* this out ;-) */
if (cp == (char *)bstr+1)
parent_lock = 0;
else
/*
* NOTE: Multiassigns are currently only supported as the first
* part of a path name. I don't like the idea of setting up
* another recursion level here just to parse them...
*
* This approach also makes symbolic links to non-fs devices
* limited, which is bad. I'll HAVE to think something up
* (so you can't for example have dev:tty -> con:0/0/640/100/tty)
*/
handler = DeviceProc (bstr + 1); /* XXX fix !!! */
parent_lock = IoErr ();
unlock_parent = 0;
if (! handler)
{
/* interesting bug.. long not noticed... */
sp->sp_Pkt.dp_Res1 = 0;
if (! strcasecmp (bstr + 1, "nil:"))
sp->sp_Pkt.dp_Res2 = 4242;
else
sp->sp_Pkt.dp_Res2 = ERROR_OBJECT_NOT_FOUND;
break;
}
}
++link_levels;
}
while (link_levels < MAXSYMLINKS);
if (link_levels == MAXSYMLINKS)
{
result = 0;
res_res2 = ERROR_TOO_MANY_LEVELS;
}
else
{
result = sp->sp_Pkt.dp_Res1;
res_res2 = sp->sp_Pkt.dp_Res2;
}
if (unlock_parent)
{
sp->sp_Pkt.dp_Port = __srwport;
sp->sp_Pkt.dp_Type = ACTION_FREE_LOCK;
sp->sp_Pkt.dp_Arg1 = parent_lock;
PutPacket(handler, sp);
__wait_sync_packet(sp);
}
else
unlock_parent = 1;
parent_lock = result;
name = next;
}
while (no_error && ! is_last);
/* yes I know it's ugly ... */
if (!no_error && res_res2 == ERROR_OBJECT_NOT_FOUND
&& dp && (dp->dvp_Flags & DVPF_ASSIGN))
goto retry_multi_assign;
do_return:
free_device_proc (dp);
/* set up Result2 so that the IoErr() works */
((struct Process *)FindTask (0))->pr_Result2 = res_res2;
syscall (SYS_sigsetmask, omask);
KPRINTF (("__plock: returning %ld, res2 = %ld.\n", result, res_res2));
return result;
}
/*
* this is somewhat similar to DeviceProc(), but with the difference that it's
* strictly passive, it won't start the handler, if it doesn't exist. This is
* vital to be able to deal with stubborn console handlers, that don't answer
* packets until they're really open..
* Returns:
* HAN_UNDEF device doesn't exist
* HAN_CLONE_DEV device exists, handler zero
* HAN_FS_DEV device exists, handler non-zero
* HAN_NOT_A_DEV exists, but is not a device
*/
#define HAN_UNDEF 0
#define HAN_CLONE_DEV 1
#define HAN_FS_DEV 2
#define HAN_NOT_A_DEV 3
static int
find_handler (char *bstr)
{
struct DosLibrary *dl;
struct RootNode *rn;
struct DosInfo *di;
struct DevInfo *dv;
extern struct ixemul_base *ixemulbase;
int res = HAN_UNDEF;
/* could probably use less drastic measures under 2.0... */
Forbid ();
dl = (struct DosLibrary *) ixemulbase->ix_dos_base;
rn = (struct RootNode *) dl->dl_Root;
di = BTOCPTR (rn->rn_Info);
for (dv = BTOCPTR (di->di_DevInfo); dv; dv = BTOCPTR (dv->dvi_Next))
{
#if 0
/* quite verbose output... */
{
char buf[255];
bcopy (BTOCPTR (dv->dvi_Name)+1, buf, *(char *)BTOCPTR (dv->dvi_Name));
buf[*(char *)BTOCPTR (dv->dvi_Name)] = 0;
KPRINTF (("\t%s(%ld,$%lx)", buf, dv->dvi_Type, dv->dvi_Task));
}
#endif
if (! strncasecmp (bstr, BTOCPTR (dv->dvi_Name), bstr[0]+1))
{
if (dv->dvi_Type == DLT_DEVICE)
res = dv->dvi_Task ? HAN_FS_DEV : HAN_CLONE_DEV;
else
res = HAN_NOT_A_DEV;
break;
}
}
Permit ();
/* KPRINTF (("\n")); */
return res;
}
/*
* feels very much like GetDeviceProc(), but works under 1.3 as well. Under
* 2.0, we're using the dos-library GetDeviceProc(), under 1.3 that is
* emulated with own structures.
* is_fs is filled out with a best guess approach, since we can't use the
* proper packet on a handler that isn't yet fully operating (as after just
* calling DevProc)
* if calling with prev!=0, is_fs is not touched.
*/
static struct DevProc *
get_device_proc (char *name, struct DevProc *prev, int *is_fs)
{
char *cp, *f;
int len;
struct DevProc *dp;
int han = HAN_UNDEF;
struct Process *this_proc;
APTR oldwin;
if (! prev)
{
/* have to prove the opposite */
*is_fs = 1;
cp = index (name, ':');
if (cp && cp != name) /* ":..." has to be a filesystem */
{
len = cp - name;
f = alloca (len + 1);
f[0] = len;
bcopy (name, f + 1, len);
/* try to find it */
han = find_handler (f);
KPRINTF ((" find_handler(%s) = %ld\n", name, han));
/* this might be wrong, we'll know more after GetDeviceProc() */
if (han == HAN_CLONE_DEV)
*is_fs = 0;
}
}
if (ix.ix_no_insert_disk_requester)
{
this_proc = (struct Process *)FindTask (0);
oldwin = this_proc->pr_WindowPtr;
this_proc->pr_WindowPtr = (APTR)-1;
}
dp = GetDeviceProc (name, prev);
if (ix.ix_no_insert_disk_requester)
this_proc->pr_WindowPtr = oldwin;
/* Second approach to verify, if a handler probably is a file system or not.
* If the device is a filesystem, but didn't contain a volume before, then
* the GetDeviceProc() call will have popped up the `please insert a disk'
* requester. If the user obeyed, the handler will now exist. On the other
* hand, if the device really is a clone device, it will still be one (ie. have
* its task field zero), so check the device list again. */
/* only for possible clone devices */
if (han == HAN_CLONE_DEV)
*is_fs = find_handler (f) != HAN_CLONE_DEV;
return dp;
}
static void
free_device_proc (struct DevProc *dp)
{
FreeDeviceProc (dp);
}
static int
unslashify (char *name)
{
char *oname = name;
/* if we're here, make sure to return every attempt to use a colon inside
* a filename as an error. I could at least imagine, that it would be
* possible for the user to actually generate a file with colons in its
* name, but he would probably not be able to get rid of it again... */
if (index (name, ':'))
return ix.ix_force_translation ? -1 : 0;
while (oname[0] == '/' && oname[1] == '/')
oname++;
/* don't (!) use strcpy () here, this is an overlapping copy ! */
if (oname > name)
bcopy (oname, name, strlen (oname) + 1);
/* This is marked as error, so that the user gets used to use `..' instead of
* `/' to really mean `parent directory', *iff* he/she enabled ix slash
* translation (this function is only called if slash translation is enabled) */
if (name[0] == '/' && name[1] == 0)
return ix.ix_force_translation ? -1 : 0;
if (name[0] == '/')
{
/* get the delimiter */
char *cp = index (name + 1, '/');
int shift = 0;
/* if there is a separating (and not terminating) slash, shift a bit ;-) */
if (cp)
while (*cp == '/')
{
shift ++;
cp ++;
}
/* is it a terminator (then discard it) or a separator ? */
if (! cp || !*cp)
{
/* terminator */
cp = name + strlen (name);
bcopy (name + 1, name, cp - name);
cp[-1-shift] = ':';
cp[-shift] = 0;
}
else
{
/* separator */
bcopy (name + 1, name, strlen (name) + 1);
cp --;
bcopy (cp, cp - (shift - 1), strlen (cp) + 1);
cp[-shift] = ':';
}
}
return 0;
}